/**
 * Copyright (c) 2007-2008 Nearchos Paspallis
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

/**
 * AspectSense
 *
 * Middleware for annotation-based, context-aware and adaptive services for
 * mobile and ubiquitous computing
 *
 * This project is an open-source effort, initiated by Nearchos Paspallis as
 * part of his PhD work at the University of Cyprus (http://www.ucy.ac.cy).
 *
 * More information about the project is available at "http://aspectsense.org".
 */

package org.aspectsense.graphs;

import java.util.Set;
import java.util.HashSet;

/**
 * Immutable implementation of a graph delta-data structure. It can be used to
 * inquire the differences between a source and a target {@link Graph}.
 *
 * @author Nearchos Paspallis [nearchos@cs.ucy.ac.cy]
 *         Date: Dec 27, 2007
 *         Time: 9:04:12 AM
 */
public class GraphDelta<E>
{
    private final Set<Node<E>> sameNodes;
    private final Set<Node<E>> addedNodes;
    private final Set<Node<E>> removedNodes;

    private final Set<DirectedEdge<E>> sameEdges;
    private final Set<DirectedEdge<E>> addedEdges;
    private final Set<DirectedEdge<E>> removedEdges;

    public GraphDelta(final Set<Node<E>> sameNodes,
                      final Set<Node<E>> addedNodes,
                      final Set<Node<E>> removedNodes,
                      final Set<DirectedEdge<E>> sameEdges,
                      final Set<DirectedEdge<E>> addedEdges,
                      final Set<DirectedEdge<E>> removedEdges)
    {
        super();

        this.sameNodes = new HashSet<Node<E>>(sameNodes);
        this.addedNodes = new HashSet<Node<E>>(addedNodes);
        this.removedNodes = new HashSet<Node<E>>(removedNodes);

        this.sameEdges = new HashSet<DirectedEdge<E>>(sameEdges);
        this.addedEdges = new HashSet<DirectedEdge<E>>(addedEdges);
        this.removedEdges = new HashSet<DirectedEdge<E>>(removedEdges);
    }

    public Set<Node<E>> getSameNodes()
    {
        return new HashSet<Node<E>>(sameNodes);
    }

    public Set<Node<E>> getAddedNodes()
    {
        return new HashSet<Node<E>>(addedNodes);
    }

    public Set<Node<E>> getRemovedNodes()
    {
        return new HashSet<Node<E>>(removedNodes);
    }

    public Set<DirectedEdge<E>> getSameEdges()
    {
        return new HashSet<DirectedEdge<E>>(sameEdges);
    }

    public Set<DirectedEdge<E>> getAddedEdges()
    {
        return new HashSet<DirectedEdge<E>>(addedEdges);
    }

    public Set<DirectedEdge<E>> getRemovedEdges()
    {
        return new HashSet<DirectedEdge<E>>(removedEdges);
    }

    public String toString()
    {
        final StringBuilder stringBuilder = new StringBuilder();

        stringBuilder.append("delta: Nodes(\n/{");

        final int numOfSameNodes = sameNodes.size();
        int snCount = 0;
        for(final Node<E> node : sameNodes)
        {
            snCount++;
            stringBuilder.append(node.getValue());
            if(snCount < numOfSameNodes) stringBuilder.append(", ");
        }

        stringBuilder.append("}\n+{");

        final int numOfAddedNodes = addedNodes.size();
        int anCount = 0;
        for(final Node<E> node : addedNodes)
        {
            anCount++;
            stringBuilder.append(node.getValue());
            if(anCount < numOfAddedNodes) stringBuilder.append(", ");
        }

        stringBuilder.append("}\n-{");

        final int numOfRemovedNodes = removedNodes.size();
        int rnCount = 0;
        for(final Node<E> node : removedNodes)
        {
            rnCount++;
            stringBuilder.append(node.getValue());
            if(rnCount < numOfRemovedNodes) stringBuilder.append(", ");
        }

        stringBuilder.append("}\n/{");

        final int numOfSameEdges = sameEdges.size();
        int seCount = 0;
        for(final DirectedEdge<E> directedEdge: sameEdges)
        {
            seCount++;
            final Node<E> fromNode = directedEdge.getFromNode();
            final Node<E> toNode = directedEdge.getToNode();

            stringBuilder.append(fromNode.getValue()).append("->").append(toNode.getValue());
            if(seCount < numOfSameEdges) stringBuilder.append(", ");
        }

        stringBuilder.append("}\n+{");

        final int numOfAddedEdges = addedEdges.size();
        int aeCount = 0;
        for(final DirectedEdge<E> directedEdge: addedEdges)
        {
            aeCount++;
            final Node<E> fromNode = directedEdge.getFromNode();
            final Node<E> toNode = directedEdge.getToNode();

            stringBuilder.append(fromNode.getValue()).append("->").append(toNode.getValue());
            if(aeCount < numOfAddedEdges) stringBuilder.append(", ");
        }

        stringBuilder.append("}\n-{");

        final int numOfRemovedEdges = removedEdges.size();
        int reCount = 0;
        for(final DirectedEdge<E> directedEdge: removedEdges)
        {
            reCount++;
            final Node<E> fromNode = directedEdge.getFromNode();
            final Node<E> toNode = directedEdge.getToNode();

            stringBuilder.append(fromNode.getValue()).append("->").append(toNode.getValue());
            if(reCount < numOfRemovedEdges) stringBuilder.append(", ");
        }

        return stringBuilder.toString();
    }
}